﻿/*
 * Wave.cs
 *
 * Created on November 17, 2007, 4:38 PM
 *
 * Copyright (C) 2007-2008  Hylke van der Schaaf
 * Copyright (C) 2010       Mathijs Miermans
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, in version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
using System;
using System.Net;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


namespace HexTD
{
	public class Wave
	{
		private List<EnemySprite> enemies = new List<EnemySprite>();
		private List<String[]> nameStrings = new List<String[]>();
		private List<int> paths = new List<int>();
		private long baseHealth;
		private int basePrice;
		private bool finalised = false;
		private Context context;
		private int level;

		private Dictionary<EnemyInstance, int> enemyCounts = new Dictionary<EnemyInstance, int>();

		public class EnemyInstance : IComparable<EnemyInstance>
		{
			public EnemyFactory.Enemy type;
			public Size size;
			public int price;
			public long health;

			public EnemyInstance(EnemyFactory.Enemy t, Size s, long h, int price)
			{
				this.type = t;
				this.size = s;
				this.health = h;
				this.price = price;
			}
			public int CompareTo(EnemyInstance o)
			{
				if (o.type == this.type && o.size == this.size && o.price == this.price && o.health == this.health)
				{
					return 0;
				}
				else if (this.health > o.health)
				{
					return 1;
				}
				else
				{
					return -1;
				}
			}

		}

		/** Creates a new instance of Wave */
		public Wave(Context context, long baseHealth, int basePrice, int level)
		{
			this.baseHealth = baseHealth;
			this.basePrice = basePrice;
			this.context = context;
			this.level = level;
		}

		public void addEnemiesFromNames(String[] names, int path)
		{
			this.nameStrings.Add(names);
			this.paths.Add(path);
		}

		private void countEnemy(EnemyInstance e)
		{
			int i = 0;
			foreach (EnemyInstance o in enemyCounts.Keys)
			{
				if (o.CompareTo(e) == 0)
				{
					i = enemyCounts[o];
					e = o;
					break;
				}
			}

			i++;
			enemyCounts.Add(e, i);
		}

		public List<EnemyInstance> enemySet()
		{
			if (!this.finalised)
			{
				this.finalise();
			}
			return new List<EnemyInstance>(enemyCounts.Keys);
		}

		public int enemyCount(EnemyFactory.Enemy e)
		{
#if PORT
			if (!this.finalised)
			{
				this.finalise();
			}

			if enemyCounts.ContainsKey(e)
			{
				return enemyCounts[e];
			}
			else
			{
				return 0;
			}
#else
			return 0;
#endif
		}
		public int enemyCount()
		{
			if (!this.finalised)
			{
				this.finalise();
			}
			return this.enemies.Count;
		}

		private void finalise()
		{
			this.enemies.Clear();
			this.enemyCounts.Clear();
			EnemyInstance tempInstance;
			for (int j = 0; j < this.nameStrings.Count; j++)
			{
				String[] names = this.nameStrings[j];
				int path = this.paths[j];
				int nr = 1;
				int count = 0;
				float prefDel;
				float totDel = this.context.getPath(path).getBaseDelay();
				for (int i = 0; i < names.Length; i++)
				{
					if (EnemyFactory.isEnemy(names[i]))
					{
						EnemyFactory.Enemy type = EnemyFactory.identifyEnemy(names[i]);
						Size s = EnemyFactory.getSize(names[i]);

						double healthMod = 1;
						float delayMod = 1;
						int number;
						switch (s)
						{
							case Size.large:
								number = (int)Math.Round(0.49f + ((float)nr) / 10f);
								healthMod = 1.0 * nr / number;
								delayMod = s.scaleMod();
								nr = number;
								break;
							case Size.small:
								number = nr * 10;
								healthMod = 0.2;
								delayMod = 0.1f;
								nr = number;
								break;
						}
						int price = Math.Max(1, (int)Math.Round(this.basePrice * healthMod));
						long health = (long)Math.Round(this.baseHealth * healthMod);

						tempInstance = new EnemyInstance(type, s, health, price);
						this.countEnemy(tempInstance);

						// Now we generate the enemies.
						for (int e = 0; e < nr; e++)
						{
							int eCount = enemies.Count;
							if (eCount > 0)
							{
								// The preffered delay is the maximum of that of this unit, and the unit before it.
								prefDel = delayMod * Math.Max(EnemyFactory.identifyEnemy(names[i]).getPreferredDelay(), enemies[eCount - 1].getPreferredDelay());
							}
							else
							{
								prefDel = delayMod * EnemyFactory.identifyEnemy(names[i]).getPreferredDelay();
							}
							totDel += prefDel;
							enemies.Add(EnemyFactory.getEnemy(names[i], this.context, path, totDel, health, price));
							count++;
						}

						nr = 1;
					}
					else
					{
						try
						{
							nr = int.Parse(names[i]);
						}
						catch
						{
							nr = 1;
						}
					}
				}
			}
			this.finalised = true;
		}

		public EnemySprite[] getEnemies()
		{
			this.finalise();
			return enemies.ToArray();
		}

		public long getBaseHealth()
		{
			return baseHealth;
		}

		public int getBasePrice()
		{
			return basePrice;
		}

		public int getLevel()
		{
			return level;
		}

		public List<String[]> getNameStrings()
		{
			return this.nameStrings;
		}
	}
}
